const PI = 3.141592653589793;

/**
 * 数学工具类
 */
export class MathUtil {

    static readonly PI2 = PI * 2;
    static readonly PI_RAD = PI / 180;
    static readonly PI_ANG = 180 / PI;

    static readonly RAD_45 = PI / 4;
    static readonly RAD_90 = PI / 2;
    static readonly RAD_135 = PI * 0.75;
    static readonly RAD_180 = PI;
    static readonly RAD_225 = PI * 1.25;
    static readonly RAD_270 = PI * 1.5;
    static readonly RAD_315 = PI * 1.75;
    static readonly RAD_360 = PI * 2;

    /**
     * 当值为偶数时返回true
     * @param val
     */
    static isEven(val: number) {
        return val % 2 == 0;
    }

    /**
     * 当值为奇数时返回true 
     * @param val 
     */
    static isOdd(val: number) {
        return val % 2 != 0;
    }

    /**
     * 将值限制在指定范围内
     * @param val 值
     * @param min 最小值
     * @param max 最大值
     */
    static clamp(val: number, min: number, max: number): number {
        return val < min ? min : val > max ? max : val;
    }

    /**
     * 将值限制在0-1范围内
     * @param val 值
     */
    static clamp01(val: number): number {
        return val < 0 ? 0 : val > 1 ? 1 : val;
    }

    /**
     * 将数值限定在一个正循环范围内
     * @param val 数值
     * @param len 循环范围 0 ~ len
     */
    static repeat(val: number, len: number): number {
        const num = Math.floor(val / len);
        return val - num * len;
    }

    /**
     * 角度转弧度
     * @param ang 角度
     */
    static ang2Rad(ang: number) {
        return ang * this.PI_RAD;
    }

    /**
     * 角度转弧度
     * @param rad 弧度
     */
    static rad2Ang(rad: number) {
        return rad * this.PI_ANG;
    }

    /**
     * 使弧度限定在0 ~ 2pi范围内
     * @param rad 弧度
     */
    static normalizeRad(rad: number) {
        return this.repeat(rad, MathUtil.PI2);
    }

    /**
     * 使角度限定在0 ~ 360范围内
     * @param ang 角度
     */
    static normalizeAng(ang: number) {
        return this.repeat(ang, 360);
    }

    /**
     * 弧度与目标弧度的差值
     * @param rad 弧度
     * @param target 目标弧度
     */
    static radDiff(rad: number, target: number) {
        let diff = this.normalizeRad(target - rad);
        if (diff > MathUtil.RAD_180) {
            diff -= MathUtil.RAD_360;
        }
        return diff;
    }

    /**
     * 角度与目标角度的差值
     * @param ang 角度
     * @param target 目标角度
     */
    static angDiff(ang: number, target: number) {
        let diff = this.normalizeAng(target - ang);
        if (diff > 180) {
            diff -= 360;
        }
        return diff;
    }

    /**
     * 弧度是否在弧度区间内
     * @param rad 弧度
     * @param rad1 弧度左区间
     * @param rad2 弧度右区间
     */
    static radBetween(rad: number, rad1: number, rad2: number) {
        if (this.radDiff(rad1, rad2) >= this.RAD_180) {
            let t = rad1;
            rad1 = rad2;
            rad2 = t;
        }
        if (rad1 <= rad2) {
            return rad >= rad1 && rad <= rad2;
        } else {
            return rad >= rad1 || rad <= rad2;
        }
    }

    /**
     * 角度是否在角度区间内
     * @param ang 角度
     * @param ang1 角度左区间
     * @param ang2 角度右区间
     */
    static angBetween(ang: number, ang1: number, ang2: number) {
        if (this.angDiff(ang1, ang2) >= 180) {
            let t = ang1;
            ang1 = ang2;
            ang2 = t;
        }
        if (ang1 <= ang2) {
            return ang >= ang1 && ang <= ang2;
        } else {
            return ang >= ang1 || ang <= ang2;
        }
    }

    /**
     * 线性插值
     * @param from 
     * @param to 
     * @param t 
     */
    static lerp(from: number, to: number, t: number) {
        const clampedT = this.clamp01(t);
        return from + (to - from) * clampedT;
    }

}